#include "Util.h"
//#include "stdio.h"
#include "windows.h"
#include "Listener.h"
#include "StarMap.h"
#include "App.h"

starEntry*	gStarMap=null;
int			gStarCount=0;
Model*		gStarModel=null;
ShaderID	gStarShader=0;

smsd*	starEntryDef[]={
	new si32("StarID"),
	new si32("HIP"),
	new si32("HD"),
	new si32("HR"),
	new sstr32("Gliese"),
	new sstr32("BayerFlamsteed"),
	new sstr32("ProperName"),
	new sf32("RA"),
	new sf32("Dec"),
	new sf32("Distance"),
	new sf32("PMRA"),
	new sf32("PMDec"),
	new sf32("RV"),
	new sf32("Mag"),
	new sf32("AbsMag"),
	new sstr32("Spectrum"),
	new sf32("ColorIndex"),
	new sf32("X"),
	new sf32("Y"),
	new sf32("Z"),
	new sf32("VX"),
	new sf32("VY"),
	new sf32("VZ"),
};


void renderStarModel(Renderer* renderer){
	renderer->setTexture(TMU0, gApp->lightFlareTexture);
	renderer->setBlending(GL_ONE, GL_ONE);
	renderer->setMask(COLOR);
	//renderer->setDepthFunc(GL_ALWAYS);
	renderer->setShader(gStarShader);
	renderer->setTexture("Base", gApp->lightFlareTexture);
	renderer->changeDepthFunc(DEPTH_NONE);
	float starScale=30.0f;
	renderer->setShaderConstant1f("time", gApp->time);
	renderer->setShaderConstant3f("screenX", gApp->screenX*starScale);
	renderer->setShaderConstant3f("screenY", gApp->screenY*starScale);
	renderer->setShaderConstant1f("blurFactor", 1.0f-gApp->blurFactor);

	renderer->apply();
//	glPushMatrix();
//	glScalef(0.01f,0.01f,0.01f);
	gStarModel->draw(renderer);
//	glPopMatrix();
}


void*	loadFile(char*	fname,int*	outSize){
	FILE*	f=0;
	if(fopen_s(&f,fname,"rb")){
		MessageBox(0,"File load error.",fname,0);
		return 0L;
	}
	fseek(f,0,SEEK_END);
	long len = ftell(f);
	fseek(f,0,SEEK_SET);
	char* fdata=(char*)malloc(len);
	if(!fdata){
		MessageBox(0,"File memory allocation error.",fname,0);
		return 0L;
	}
	if(!fread(fdata,len,1,f)){
		MessageBox(0,"File read error.",fname,0);
		fclose(f);
		return 0L;
	}
	fclose(f);
	*outSize=len;
	return fdata;
}


void*	loadCSVdata(char*	fname,smsd**	fields,int	fieldCount,int entrySize,int*	outElemCount){
	FILE*	f=0;
	char	pathBuf[1024];
	sprintf_s(pathBuf,1024,"%s.csv",fname);
	
	if(fopen_s(&f,pathBuf,"rb")){
		MessageBox(0,"CSV Load error.","Couldn't open csv file..",0);
		return 0L;
	}
	fseek(f,0,SEEK_END);
	long len = ftell(f);
	fseek(f,0,SEEK_SET);
	char* fdata=(char*)malloc(len);
	if(!fdata){
		MessageBox(0,"CSV Load error.","Couldn't allocate csv buffer..",0);
		return 0L;
	}
	if(!fread(fdata,len,1,f)){
		MessageBox(0,"CSV Load error.","Couldn't read csv file..",0);
		fclose(f);
		return 0L;
	}
	fclose(f);
	bool	incrlf=false;
	char*	lineStart=fdata;
	int		entryCount=0;

	for(int i=0;i<len;i++){
		if(fdata[i]=='\r' || fdata[i]=='\n'){
			if(incrlf==false){
				fdata[i]=',';
				incrlf=true;
				entryCount++;
			}
		}else{
			if(incrlf){
				lineStart=fdata;
				incrlf=false;
			}
			if(fdata[i]==',')fdata[i]=0x00;
		}
	}
	incrlf=false;
	lineStart=fdata;
	void*	data=malloc(entrySize*(entryCount-1));
	void*	entry=data;
	bool	skipFirst=true;

	for(int i=0;i<len;i++){
		if(fdata[i]==','){
			void*	out=entry;
			//Parse the linebuffer
			if(skipFirst){
				skipFirst=false;
				for(int fi=0;fi<fieldCount;fi++){
					while(*lineStart!=',')lineStart++;
				}
			}else{
				fdata[i]=0x00;
				for(int fi=0;fi<fieldCount;fi++){
					out=fields[fi]->parse(lineStart,out);
					while(*lineStart)lineStart++;
					lineStart++;
				}
				entry=(void*)(((char*)entry)+entrySize);
			}
			lineStart=&fdata[i+1];
		}else{
			if(fdata[i]=='\n'||fdata[i]=='\r')lineStart=&fdata[i+1];
		}
	}
	MessageBox(0,"CSV Parsed.",fname,0);
	sprintf_s(pathBuf,1024,"%s.bin",fname);
	if(fopen_s(&f,pathBuf,"wb")){
		MessageBox(0,"CSV Parse error.","Couldn't open binary file for write..",0);
		fclose(f);
		return 0L;
	}
	int wsz=entrySize*(entryCount-1);
	if(!fwrite(data,wsz,1,f)){
		MessageBox(0,"CSV Parse error.","Couldn't write binary file..",0);
		fclose(f);
		return 0L;
	}
	*outElemCount=entryCount-1;

	fclose(f);
	return data;
}

bool buildStarModel(Renderer*	renderer){
	if(fileExists("Media/Models/starMap.mesh.model")==false){
		if(fileExists("Media/Starcat/hygxyz.bin")==false)//false
		{
			gStarMap=(starEntry*)loadCSVdata("Media/Starcat/hygxyz",&starEntryDef[0],array_size(starEntryDef),sizeof(starEntry),&gStarCount);
		}else{
			int fsize=0;
			gStarMap=(starEntry*)loadFile("Media/Starcat/hygxyz.bin",&fsize);
			gStarCount=fsize/sizeof(starEntry);
		}
	}
	if((gStarShader = getShader("star.shd")) == SHADER_NONE){
		return false;
	}
	gStarModel=new Model();
	if(!loadModel("starMap.mesh",gStarModel))
	{
		delete gStarModel;

		int		vct=gStarCount*4;
		float*	verts=new  float[vct*3];
		float*	uvs=new  float[vct*2];
		int		vict=gStarCount*6;
		int*	index=new int[vict];

		for(int i=0;i<gStarCount;i++){
			starEntry& st=gStarMap[i];
			int vi=i*4*3;
			int ti=i*4*2;
			setf3(&verts[vi]  ,st.X,st.Y,st.Z);
			setf3(&verts[vi+3],st.X,st.Y,st.Z);
			setf3(&verts[vi+6],st.X,st.Y,st.Z);
			setf3(&verts[vi+9],st.X,st.Y,st.Z);

			for(int t=vi;t<vi+12;t++){
				verts[t]*=200.0;
			}
			setf2(&uvs[ti],-0.5,-0.5);
			setf2(&uvs[ti+2], 0.5,-0.5);
			setf2(&uvs[ti+4], 0.5, 0.5);
			setf2(&uvs[ti+6],-0.5, 0.5);
			//setf2(&uvs[ti],0.0,0.0);
			//setf2(&uvs[ti+2], 1.0,0.0);
			//setf2(&uvs[ti+4], 1.0, 1.0);
			//setf2(&uvs[ti+6],0.0, 1.0);
		}
		int ibld=0;
		for(int i=0;i<gStarCount*4;i+=4){
			//012230
			index[ibld++]=i+0;
			index[ibld++]=i+1;
			index[ibld++]=i+2;
			index[ibld++]=i+2;
			index[ibld++]=i+3;
			index[ibld++]=i+0;
		}
		Renderer *renderer=gApp->getRenderer();
		gStarModel=loadVertTexIndex(verts,vct,uvs,index,vict,"starMap.mesh");
	}
	if (!gStarModel->makeDrawable(gApp->getRenderer())){
		return false;
	}
	return true;
}


class StarmapListener : public Listener
{
public:
	virtual bool	message(const char *msg=null,void*	params=null){
		if(msg=="buildStarModel"){
			if(!buildStarModel((Renderer*) params))
				return false;
		}else if(msg=="renderStarModel"){
			renderStarModel((Renderer*) params);
		}
		return true;
	}
	StarmapListener(){
		Listener::listen(this,"buildStarModel");
		Listener::listen(this,"renderStarModel");
	}
};

StarmapListener	gStarmapListener;